/*
  © 2022 BLACK SESAME TECHNOLOGIES
 All rights reserved.
 (This product is licensed to: [Hanhai-ADSP Autonomous Driving Solution Platform]
 This computer software is protected by copyright law and international treaties.
 Use, reproduction, or distribution of this software is subject to the license agreement between you
 and Black Sesame Technologies. Unauthorized reproduction or distribution of this program, or any
 portion of it, may result in severe civil and criminal penalties and will be prosecuted to the
 maximum extent possible under the law.
*/

#include <iostream>
#include "dal/allocator.h"
#include "dal/error_handling.h"
#include "dal/domain_participant.h"
#include "dal_greenstonedds/converter/StatusConverter.hpp"
#include "dal_greenstonedds/converter/StatusMaskConverter.hpp"
#include "dal_greenstonedds/converter/QosConverter.hpp"
#include "dal_greenstonedds/converter/RetTypeConverter.hpp"
#include "dal_greenstonedds/converter/GuidConverter.hpp"
#include "dal_greenstonedds/listener_impl/DomainParticipantListenerImpl.hpp"
#include "dal_greenstonedds/entity_impl/DalDomainParticipantImpl.hpp"
#include "dal_greenstonedds/utils/log.hpp"
#include "dal_greenstonedds/dal/GsddsConfigLoaderInfo.hpp"

using namespace DDS;
using namespace dds::core;
using namespace dds::domain;

extern GsddsConfigLoaderInfo gs_conf;

// PARTICIPANT
dal_domain_participant_listener_t*
dal_create_domain_participant_listener(dal_domain_participant_listener_callbacks_t callbacks)
{
    dal_domain_participant_listener_t* listener = dal_domain_participant_listener_allocate();
    if (listener == nullptr) {
        return nullptr;
    }
    listener->handle_ = new DomainParticipantListenerImpl(callbacks);
    if (!listener->handle_) {
        LOG_ERROR() << "DomainParticipantListenerImpl memory allocation failed." << LOG_ENDL;
        dal_domain_participant_listener_free(listener);
        return nullptr;
    }

    return listener;
}

dal_ret_t dal_delete_domain_participant_listener(dal_domain_participant_listener_t* listener)
{
    if (listener == nullptr || listener->handle_ == nullptr) {
        LOG_ERROR() << "can't delete domain participant listener because it's nullptr." << LOG_ENDL;
        return DAL_RETCODE_PRECONDITION_NOT_MET;
    }

    DomainParticipantListenerImpl* listener_impl =
        reinterpret_cast<DomainParticipantListenerImpl*>(listener->handle_);
    if (listener_impl->ref_count_.Get() > 0) {
        LOG_ERROR() << "can't delete domain participant listener because it's entity still exist."
                    << LOG_ENDL;
        return DAL_RETCODE_PRECONDITION_NOT_MET;
    }

    delete listener_impl;
    dal_domain_participant_listener_free(listener);
    return DAL_RETCODE_OK;
}

dal_domain_participant_t* dal_create_domain_participant(dal_domain_id domain_id,
                                                        dal_domain_participant_qos_t* qos,
                                                        dal_domain_participant_listener_t* listener,
                                                        dal_status_mask_t mask)
{
    dal_domain_participant_t* d_participant = dal_domain_participant_allocate();
    if (d_participant == nullptr) {
        return nullptr;
    }
    dal_ret_t d_ret = DAL_RETCODE_OK;

    GsParticipantQosPtr g_qos = std::make_shared<DomainParticipantQos>();
    if (gs_conf.participant_qos) {
        g_qos = gs_conf.participant_qos;
    }

    d_ret = QosConverter::GetInstance()->ConvertParticipantQosFromDalToGsdds(qos, g_qos);
    if (DAL_RETCODE_OK != d_ret) {
        LOG_ERROR_WITH_LOCATION() << "convert domain participant qos failed." << LOG_ENDL;
        dal_domain_participant_free(d_participant);
        return nullptr;
    }

    StatusMask g_mask;
    d_ret = StatusMaskConverter::GetInstance()->ConvertStatusMaskFromDalToGsdds(mask, &g_mask);
    if (DAL_RETCODE_OK != d_ret) {
        LOG_ERROR_WITH_LOCATION() << "convert domain participant statusmask failed." << LOG_ENDL;
        dal_domain_participant_free(d_participant);
        return nullptr;
    }

    DomainParticipantListenerImpl* g_listener =
        (listener && listener->handle_) ?
            reinterpret_cast<DomainParticipantListenerImpl*>(listener->handle_) :
            nullptr;

    DomainParticipantFactory* factory = DomainParticipantFactory::get_instance();
    DomainParticipant* g_participant =
        factory->create_participant(domain_id, *g_qos, nullptr, mask);
    if (!g_participant) {
        LOG_ERROR_WITH_LOCATION() << "create domain_participant failed." << LOG_ENDL;
        dal_domain_participant_free(d_participant);
        return nullptr;
    }

    d_participant->handle_ = new DalDomainParticipantImpl(g_participant, g_listener);
    return d_participant;
}

dal_ret_t dal_delete_domain_participant(dal_domain_participant_t* participant)
{
    if (participant == nullptr) {
        LOG_ERROR_WITH_LOCATION() << "Double free." << LOG_ENDL;
        return DAL_RETCODE_OK;
    }
    if (participant->handle_ == nullptr) {
        dal_domain_participant_free(participant);
        return DAL_RETCODE_OK;
    }

    DalDomainParticipantImpl* participant_impl =
        reinterpret_cast<DalDomainParticipantImpl*>(participant->handle_);
    dds::domain::DomainParticipant* g_participant = participant_impl->GsddsDomainParticipant();

    uint32_t g_ret =
        dds::domain::DomainParticipantFactory::get_instance()->delete_participant(g_participant);
    dal_ret_t d_ret = RetTypeConverter::GetInstance()->ConvertRetFromGsddsToDal(g_ret);

    if (DAL_RETCODE_OK != d_ret) {
        LOG_ERROR_WITH_LOCATION() << "delete domain participant failed." << LOG_ENDL;
        return d_ret;
    }

    delete participant_impl;
    dal_domain_participant_free(participant);
    return DAL_RETCODE_OK;
}

dal_guid_t dal_domain_participant_get_guid(dal_domain_participant_t* participant)
{
    dal_guid_t guid = { };
    if (participant == nullptr || participant->handle_ == nullptr) {
        LOG_ERROR_WITH_LOCATION() << "Invalid domain participant." << LOG_ENDL;
        return guid;
    }
    auto g_participant =
        reinterpret_cast<DalDomainParticipantImpl*>(participant->handle_)->GsddsDomainParticipant();
    auto g_guid = GuidConverter::GetInstance()->FetchGuidFromInstanceHandle(
        g_participant->get_instance_handle());
    guid = GuidConverter::GetInstance()->ConvertGuidFromGsddsToDal(g_guid);
    return guid;
}

void dal_domain_participant_listener_set_participant_monitor_callback(
    dal_domain_participant_listener_t* listener,
    dal_participant_monitor_callback callback,
    void* callback_data)
{
    auto listener_impl = reinterpret_cast<DomainParticipantListenerImpl*>(listener->handle_);
    listener_impl->set_participant_monitor_callback(callback);
    listener_impl->set_participant_monitor_callback_data(callback_data);
}
